package com.jiang.demo.aspect;

import com.alibaba.fastjson.JSON;
import com.jiang.demo.common.constans.Constant;
import com.jiang.demo.common.MSGException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * ---------------------------------------------------------------------------------------------------------------
 * - 2024-06-01 15:24 - jiangshaoneng@163.com -
 * ---------------------------------------------------------------------------------------------------------------
 * -      ██╗██╗ █████╗ ███╗   ██╗ ██████╗ ███████╗██╗  ██╗ █████╗  ██████╗ ███╗   ██╗███████╗███╗   ██╗ ██████╗
 * -      ██║██║██╔══██╗████╗  ██║██╔════╝ ██╔════╝██║  ██║██╔══██╗██╔═══██╗████╗  ██║██╔════╝████╗  ██║██╔════╝
 * -      ██║██║███████║██╔██╗ ██║██║  ███╗███████╗███████║███████║██║   ██║██╔██╗ ██║█████╗  ██╔██╗ ██║██║  ███╗
 * - ██   ██║██║██╔══██║██║╚██╗██║██║   ██║╚════██║██╔══██║██╔══██║██║   ██║██║╚██╗██║██╔══╝  ██║╚██╗██║██║   ██║
 * - ╚█████╔╝██║██║  ██║██║ ╚████║╚██████╔╝███████║██║  ██║██║  ██║╚██████╔╝██║ ╚████║███████╗██║ ╚████║╚██████╔╝
 * -  ╚════╝ ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝╚═╝  ╚═══╝ ╚═════╝
 * ---------------------------------------------------------------------------------------------------------------
 * 打印接口请求参数+返回参数+接口请求耗时
 * ---------------------------------------------------------------------------------------------------------------
 */
@Slf4j
@Component
@Aspect
@Order(1)
public class LogAspect {

    // 指定切面生效的包
    @Pointcut("execution(public * com.jiang.demo.controller.*.*(..))")
    public void logPointCut() {}

    @Around("logPointCut()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        // 对于每个请求生成唯一ID,用作日志跟踪。对应到logback-spring.xml中trace_id
        MDC.put(Constant.TRACE_ID, UUID.randomUUID().toString().replace("-",""));

        // 获取请求信息request: resource+method 等
        RequestAttributes requestAttributes = Objects.requireNonNull(RequestContextHolder.getRequestAttributes());
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        String resource = request.getRequestURI();
        String method = request.getMethod();

        //请求头
        Map<String, String> headerMap = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            String value = request.getHeader(name);
            headerMap.put(name, value);
        }
        String header = JSON.toJSONString(headerMap);

        //请求参数
        Map<String, String> paramsMap = new HashMap<>();
        for (Map.Entry<String, String[]> paramEntry : request.getParameterMap().entrySet()) {
            paramsMap.put(paramEntry.getKey(), paramEntry.getValue()!=null? paramEntry.getValue()[0] : "");
        }
        String params = JSON.toJSONString(paramsMap);
        if ("{}".equals(params) && Arrays.asList("POST", "PUT", "PATCH").contains(method)) {
            Object[] args = pjp.getArgs();
            for (Object obj : args) {
                if(obj instanceof HttpServletRequest) continue;
                if(obj instanceof MultipartFile) continue;
                params = JSON.toJSONString(obj);
            }
        }

        //用户信息 判断请求头中是否包含token, 存在则根据token解析出userName, 不存在则认为是无需token验证的接口
        String username = request.getHeader("Jiang-Client-Username");
        String token = request.getHeader("Jiang-Client-Token");

        Object ob;      // 返回结果
        long timing;    // 请求耗时
        log.info("[{} resource:{}] params:{} header:{}", method, resource, params, header);
        long start = System.currentTimeMillis();
        try {
            ob = pjp.proceed();
            timing = System.currentTimeMillis() - start;
            log.info("[{} resource:{}] result:{}; timing:{}", method, resource, JSON.toJSONString(ob), timing);
        }catch (Exception e){
            timing = System.currentTimeMillis() - start;
            String result = e.getMessage();
            if(e instanceof MSGException){
                result = ((MSGException) e).getCode() + ":"+ result;
            }
            log.info("[{} resource:{}] result:{}; timing:{}", method, resource, result, timing);
            throw e;
        }
        return ob;
    }

}


